﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using SNSHelper.Common;
using CommonManage = DevelopStudio.Common.Manage;
using CommonBasic = DevelopStudio.Common.Basic;
using SharpSvn;
using System.Collections.ObjectModel;
using System.Security;
using System.Security.Authentication;
using DevelopStudio.Basic;
using System.Text.RegularExpressions;

namespace DevelopStudio.Basic
{
    public static class Utility
    {
        static Utility()
        {
            InitLoad();
        }

        /// <summary>
        /// DevelopStudioByFiredragon
        /// </summary>
        public static readonly byte[] EncryptKey = new byte[] {
        68,
        101,
        118,
        101,
        108,
        111,
        112,
        83,
        116,
        117,
        100,
        105,
        111,
        66,
        121,
        70,
        105,
        114,
        101,
        100,
        114,
        97,
        103,
        111,
        110
        };

        public static string EncryptKeyString { get; set; }

        public static EnvironmentSettings Settings { get; set; }

        public static HttpHelper HttpHelper { get; set; }

        public static SharpSvn.SvnClient SvnClient { get; set; }

        public static KeyValuePair<string, string> AuthenticityToken { get; set; }

        private static bool signIn = false;

        public static bool SignIn
        {
            get
            {
                return signIn;
            }

            private set
            {
                signIn = value;
            }
        }

        #region HtmlHelper

        public static string GetHtml(string url, RedmineInfo redmineInfo = null)
        {
            if ((redmineInfo == Utility.Settings.Redmine ||
                redmineInfo == null)&&
                Utility.SignIn == false)
            {
                string message;
                if (Utility.Login(out message, redmineInfo) == false)
                {
                    return string.Empty;
                }
            }

            return Utility.HttpHelper.GetHtml(url);
        }

        public static string GetHtml(string url, string postData, bool isPost, RedmineInfo redmineInfo = null)
        {
            if (redmineInfo == Utility.Settings.Redmine &&
                Utility.SignIn == false)
            {
                string message;
                if (Utility.Login(out message, redmineInfo) == false)
                {
                    return string.Empty;
                }
            }

            return Utility.HttpHelper.GetHtml(url, string.Empty, postData, isPost);
        }

        public static string GetHtml(string url, string referer, string postData, bool isPost, RedmineInfo redmineInfo = null)
        {
            if (redmineInfo == Utility.Settings.Redmine &&
                Utility.SignIn == false)
            {
                string message;
                if (Utility.Login(out message, redmineInfo) == false)
                {
                    return string.Empty;
                }
            }

            return Utility.HttpHelper.GetHtml(url, referer, postData, isPost);
        }

        #endregion

        #region Login authority

        public static bool Login(out string message, RedmineInfo redmineInfo = null)
        {
            if (redmineInfo == null)
            {
                redmineInfo = Utility.Settings.Redmine;
            }

            string url = redmineInfo.BasicInfo.BaseUrl;
            string usename = redmineInfo.BasicInfo.UserName;
            string password = redmineInfo.BasicInfo.Password;

            // Retry 6 times.
            int i = 6;
            while (i >= 0)
            {
                --i;
                message = string.Empty;
                Utility.HttpHelper.cookieContainer = new System.Net.CookieContainer();
                string baseContent = Utility.HttpHelper.GetHtml(url);
                Dictionary<string, string> properties = Utility.ParseRootHtml(baseContent);

                if (properties.ContainsKey("authenticity_token") == false ||
                    properties.ContainsKey("login") == false ||
                    string.IsNullOrEmpty(properties["authenticity_token"]) == true ||
                    string.IsNullOrEmpty(properties["login"]) == true)
                {
                    message = Properties.Resources.ErrorTestLogin;
                    return false;
                }

                Utility.AuthenticityToken = new KeyValuePair<string, string>("authenticity_token", properties["authenticity_token"]);

                string baseUrl = url.TrimEnd('/');
                string postData = "authenticity_token=" + properties["authenticity_token"];
                postData += "&back_url=" + Utility.GetAbsoluteUrl(AppConst.MyAccountUrl, baseUrl).Replace(":", "%253A").Replace("/", "%252F");
                postData += "&username=" + usename;
                postData += "&password=" + password;
                postData += "&login=" + properties["login"];
                baseContent = Utility.HttpHelper.GetHtml(Utility.GetAbsoluteUrl(AppConst.LoginUrl, baseUrl), postData, true);
                properties = Utility.ParseMyAccountPageXml(baseContent, redmineInfo.Projects);

                if (properties.ContainsKey("id") == true &&
                    string.IsNullOrEmpty(properties["id"]) == false)
                {
                    message = string.Format(Properties.Resources.SuccessLogin, usename, properties["name"]);
                    Utility.signIn = redmineInfo == Utility.Settings.Redmine ? true : false;
                    Settings.MyInfo = new MemberInfo() { ID = properties["id"], Name = properties["name"], Email = properties["email"] };

                    return true;
                }
            }

            message = Properties.Resources.ErrorTestLogin;
            return false;
        }

        public static void Logout()
        {
            //HttpHelper.SignOut();
            Utility.HttpHelper = new SNSHelper.Common.HttpHelper();
            Utility.signIn = false;
        }

        public static Dictionary<TKey, TValue> MergeDictionary<TKey, TValue>(this Dictionary<TKey, TValue> first, Dictionary<TKey, TValue> second)
        {
            Dictionary<TKey, TValue> Rtn = new Dictionary<TKey, TValue>();
            var collection = first.Union(second);

            foreach (var item in collection)
            {
                Rtn[item.Key] = item.Value;
            }

            return Rtn;
        }

        public static void GetMembersInfo()
        {
            GetMembersInfo(Utility.Settings.Redmine);
        }

        public static void GetMembersInfo(RedmineInfo redminInfo)
        {
            foreach (ProjectInfo var in redminInfo.Projects)
            {
                string baseContent = Utility.GetHtml(Utility.GetAbsoluteUrl(var.Url, redminInfo.BasicInfo.BaseUrl), redminInfo);
                Dictionary<string, string> users = Utility.ParseProjectUsersPageXml(baseContent);
                redminInfo.AddMembers(users, var.ID);
            }

            MemberInfo myinfo = redminInfo.Members.Find(o => o.ID == Utility.Settings.MyInfo.ID);

            if (myinfo != null)
            {
                Utility.Settings.MyInfo.Name = myinfo.Name;
            }
        }

        #endregion

        #region Initialize Load

        public static void InitLoad()
        {
            LoadEnvironmentSettings();

            HttpHelper = new HttpHelper();

            SvnClient = new SharpSvn.SvnClient();

            EncryptKeyString = System.Text.Encoding.UTF8.GetString(Utility.EncryptKey);
        }

        public static EnvironmentSettings LoadEnvironmentSettings()
        {
            if (Settings == null)
            {
                try
                {
                    Settings = CommonManage.Utility.SerializedFile2Object<EnvironmentSettings>(Utility.GetAbsolutePath(AppConst.EnvironmentSettingsPath));
                }
                finally
                {
                    if (Settings == null)
                    {
                        Settings = new EnvironmentSettings()
                        {
                            WorkingCopies = new List<WorkingCopyInfo>(),
                            Redmine = new RedmineInfo()
                        };
                    }
                }
            }

            Assembly assembly = Assembly.GetExecutingAssembly();

            Settings.Version = assembly.GetName().Version;

            return Settings;
        }

        public static void SaveEnvironmentSettings()
        {
            string path = Utility.GetAbsolutePath(AppConst.EnvironmentSettingsPath);

            if (File.Exists(path) == false)
            {
                Utility.CreateFolder(path);
            }

            CommonManage.Utility.SerializedObject2File(Settings, path);
        }

        public static void CreateFolder(string path)
        {
            string[] folders = path.Split('\\');

            string newPath = folders[0];
            folders[0] = string.Empty;

            string fileName;

            if (folders[folders.Length - 1].Contains(".") == true)
            {
                fileName = folders[folders.Length - 1];
                folders[folders.Length - 1] = string.Empty;
            }
            else
            {
                fileName = string.Empty;
            }

            foreach (string var in folders)
            {
                newPath = string.Format("{0}{1}{2}", newPath, Path.DirectorySeparatorChar, var);

                if (Directory.Exists(newPath) == false)
                {
                    Directory.CreateDirectory(newPath);
                }
            }

            if (string.IsNullOrEmpty(fileName) == false)
            {
                newPath = string.Format("{0}{1}{2}", newPath, Path.DirectorySeparatorChar, fileName);

                using (FileStream fs = File.Create(newPath))
                {
                }
            }
        }

        #endregion

        #region Absolute Path

        public static string GetAbsolutePath(string relativePath)
        {
            return Path.GetFullPath(Path.Combine(Application.StartupPath, relativePath));
        }

        public static string GetAbsoluteUrl(string relativeUrl, string baseUrl = null)
        {
            if (string.IsNullOrEmpty(baseUrl) == true)
            {
                baseUrl = Utility.Settings.Redmine.BasicInfo.BaseUrl;
            }

            return (new Uri(new Uri(baseUrl), relativeUrl)).AbsoluteUri;
        }

        #endregion 

        #region String Find

        public static string GetInnerText(string strSrc, string tagName, string id)
        {
            int endIndex = 0;

            string inner = GetInnerText(strSrc, tagName, id, ref endIndex);

            if (endIndex == -1)
            {
                return null;
            }

            return inner;
        }

        public static string GetInnerText(string strSrc, string tagName, string id, ref int endIndex)
        {
            int startIndex = 0;
            string strStart = ">";
            string strEnd = "</" + tagName;
            string idProperty = "id=\"" + id + "\"";

            while(true)
            {
                startIndex = strSrc.IndexOf(tagName, endIndex, StringComparison.CurrentCultureIgnoreCase);
                if (startIndex == -1)
                {
                    endIndex = startIndex;
                    return string.Empty;
                }

                endIndex = strSrc.IndexOf(strStart, startIndex, StringComparison.CurrentCultureIgnoreCase);
                if (endIndex == -1)
                {
                    return string.Empty;
                }

                string property = strSrc.Substring(startIndex, endIndex - startIndex);

                if (property.Contains(idProperty) == false)
                {
                    continue;
                }

                if (property[property.Length - 1] == '/')
                {
                    return string.Empty;
                }

                endIndex += strStart.Length;
                startIndex = endIndex;

                endIndex = strSrc.IndexOf(strEnd, startIndex, StringComparison.CurrentCultureIgnoreCase);

                if (endIndex == -1)
                {
                    return string.Empty;
                }

                return strSrc.Substring(startIndex, endIndex - startIndex);
            }
        }

        public static string GetMidString(string strSrc, string strStart, string strEnd)
        {
            int endIndex = 0;

            string inner = GetMidString(strSrc, strStart, strEnd, ref endIndex);

            if (endIndex == -1)
            {
                return null;
            }

            return inner;
        }

        public static string GetMidString(string strSrc, string strStart, string strEnd, ref int endIndex)
        {
            int startIndex = 0;
            startIndex = strSrc.IndexOf(strStart, endIndex, StringComparison.CurrentCultureIgnoreCase);

            if (startIndex == -1)
            {
                endIndex = startIndex;
                return string.Empty;
            }
            startIndex += strStart.Length;
            if (strEnd == string.Empty)
            {
                endIndex = startIndex;
                return strSrc.Substring(startIndex);
            }

            endIndex = strSrc.IndexOf(strEnd, startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (endIndex == -1)
            {
                return string.Empty;
            }

            return strSrc.Substring(startIndex, endIndex - startIndex);
        }

        public static string GetValueById(string strSrc, string id, ref int endIndex)
        {
            int startIndex = 0;

            string strStart = "id=\"" + id;

            startIndex = strSrc.IndexOf(strStart, endIndex, StringComparison.CurrentCultureIgnoreCase);

            if (startIndex == -1)
            {
                endIndex = startIndex;
                return string.Empty;
            }

            startIndex += strStart.Length;
            strStart = "value=\"";

            startIndex = strSrc.IndexOf(strStart, startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (startIndex == -1)
            {
                endIndex = startIndex;
                return string.Empty;
            }

            startIndex += strStart.Length;

            endIndex = strSrc.IndexOf("\"", startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (endIndex == -1)
            {
                return string.Empty;
            }

            return strSrc.Substring(startIndex, endIndex - startIndex);
        }

        public static string GetValueByName(string strSrc, string id, ref int endIndex)
        {
            int startIndex = 0;

            string strStart = "name=\"" + id;

            startIndex = strSrc.IndexOf(strStart, endIndex, StringComparison.CurrentCultureIgnoreCase);

            if (startIndex == -1)
            {
                endIndex = startIndex;
                return string.Empty;
            }

            startIndex += strStart.Length;
            strStart = "value=\"";

            startIndex = strSrc.IndexOf(strStart, startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (startIndex == -1)
            {
                endIndex = startIndex;
                return string.Empty;
            }

            startIndex += strStart.Length;

            endIndex = strSrc.IndexOf("\"", startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (endIndex == -1)
            {
                return string.Empty;
            }

            return strSrc.Substring(startIndex, endIndex - startIndex);
        }

        public static string GetHtmlandFirstString(string strUrl, out string strHtml, string strStart, string strEnd, ref int endIndex)
        {
            int startIndex = -1;
            int whCnt = 0;

            strHtml = string.Empty;

            while ((startIndex == -1) && (whCnt < 2))
            {
                ++whCnt;

                strHtml = Utility.HttpHelper.GetHtml(strUrl);

                if (strStart == string.Empty)
                {
                    endIndex = 0;
                    return string.Empty;
                }
                startIndex = strHtml.IndexOf(strStart, StringComparison.CurrentCultureIgnoreCase);
            }

            if (startIndex == -1)
            {
                endIndex = -1;
                return string.Empty;
            }

            startIndex += strStart.Length;

            if (strEnd == string.Empty)
            {
                endIndex = startIndex;
                return string.Empty;
            }

            endIndex = strHtml.IndexOf(strEnd, startIndex, StringComparison.CurrentCultureIgnoreCase);

            if (endIndex == -1)
            {
                return string.Empty;
            }

            return strHtml.Substring(startIndex, endIndex - startIndex);
        }

        #endregion

        #region Parse Html

        public static Dictionary<string, string> ParseRootHtml(string html)
        {
            Dictionary<string, string> pageData = new Dictionary<string, string>();

            if (string.IsNullOrEmpty(html) == true)
            {
                return pageData;
            }

            int index = 0;

            try
            {
                string authenticity_token = Utility.GetMidString(html, "<input name=\"authenticity_token\" type=\"hidden\" value=\"", "\"", ref index);

                pageData["authenticity_token"] = authenticity_token;

                if (index == -1)
                {
                    return pageData;
                }

                string login = Utility.GetMidString(html, "name=\"login\" value=\"", "\"", ref index);

                pageData["login"] = login;
            }
            catch (ArgumentOutOfRangeException)
            {
                return pageData;
            }

            return pageData;
        }

        public static Dictionary<string, string> ParseMyAccountPageXml(string html, List<ProjectInfo> projects)
        {
            Dictionary<string, string> pageData = new Dictionary<string, string>();

            int index = 0;

            try
            {
                string id = Utility.GetMidString(html, "<a href=\"/users/", "\"", ref index);

                pageData["id"] = id;

                if (index == -1)
                {
                    return pageData;
                }

                #region 解析project info

                string options = Utility.GetMidString(html, "<select", "</select>", ref index);

                if (index == -1)
                {
                    return pageData;
                }

                int tempIndex = 0;

                while (true)
                {
                    string value = Utility.GetMidString(options, "<option value=", ">", ref tempIndex);

                    if (tempIndex == -1)
                    {
                        break;
                    }

                    string name = Utility.GetMidString(options, ">", "</option>", ref tempIndex);

                    if (tempIndex == -1)
                    {
                        break;
                    }

                    value = value.Trim('\'').Trim('\"');

                    if (string.IsNullOrEmpty(value) == true ||
                        value[0] == ' ')
                    {
                        continue;
                    }

                    ProjectInfo project = projects.Where(o => o.Url == value).FirstOrDefault();

                    if (project == null)
                    {
                        project = new ProjectInfo();
                        project.Url = value;
                        project.Name = name;
                        projects.Add(project);
                    }
                    else
                    {
                        project.Name = name;
                    }
                }

                #endregion

                string user_firstname = Utility.GetValueById(html, "user_firstname", ref index);
                string user_lastname = Utility.GetValueById(html, "user_lastname", ref index);
                pageData["name"] = user_lastname + user_firstname;
                pageData["email"] = Utility.GetValueById(html, "user_mail", ref index);
            }
            catch (ArgumentOutOfRangeException)
            {
                return pageData;
            }

            return pageData;
        }

        public static Dictionary<string, string> ParseProjectUsersPageXml(string html)
        {
            Dictionary<string, string> pageData = new Dictionary<string, string>();

            int index = 0;

            try
            {
                while (index != -1)
                {
                    string id = Utility.GetMidString(html, "<a href=\"/users/", "\"", ref index);

                    if (index == -1)
                    {
                        break;
                    }

                    string name = Utility.GetMidString(html, ">", "</a>", ref index);

                    if (index == -1)
                    {
                        break;
                    }

                    pageData[id] = name;
                }

            }
            catch (ArgumentOutOfRangeException)
            {
                return pageData;
            }

            return pageData;
        }

        private static string GetBugPageHtml(string bugID)
        {
            if (Utility.SignIn == false)
            {
                string errorMessage;
                if (Utility.Login(out errorMessage) == false)
                {
                    throw new AuthenticationException(errorMessage);
                }
            }

            string url = Settings.Redmine.BasicInfo.BaseUrl + string.Format(AppConst.BugsPageXmlUrl, bugID);

            return Utility.HttpHelper.GetHtmlXml(url, false);
        }

        public static SearchBugs GetSearchBugs(string userID)
        {
            if (Utility.SignIn == false)
            {
                string errorMessage;
                if (Utility.Login(out errorMessage) == false)
                {
                    throw new AuthenticationException(errorMessage);
                }
            }
            string bugSearchUrl = Utility.GetAbsoluteUrl(string.Format(AppConst.BugSearchPageXmlUrl, Utility.Settings.MyInfo.ID));

            return CommonManage.Utility.SerializedXml2Object<SearchBugs>(Utility.HttpHelper.GetHtmlXml(bugSearchUrl, false));
        }

        public static bool IsOpenBugStatus(BugInfo bugInfo, out string message)
        {
            message = string.Empty;
            int status = bugInfo.Status.GetID();

            if (status != (int)AppEnum.BugStatus.Created &&
                status != (int)AppEnum.BugStatus.Reopen &&
                status != (int)AppEnum.BugStatus.Fixing)
            {
                message = string.Format(Properties.Resources.ErrorNoSupportAssignedBug, bugInfo.ID, bugInfo.Status.Name);
                return false;
            }

            return true;
        }

        public static bool IsMyBugs(BugInfo bugInfo, string uid, out string message)
        {
            message = string.Empty;

            if (bugInfo.AssignedTo == null)
            {
                message = string.Format(Properties.Resources.ErrorNotYourBugs, bugInfo.ID, Properties.Resources.Nobody);
                return false;
            }

            if (bugInfo.AssignedTo.ID != uid)
            {
                message = string.Format(Properties.Resources.ErrorNotYourBugs, bugInfo.ID, bugInfo.AssignedTo.Name);
                return false;
            }

            return true;
        }

        public static bool SupportCodeReviewBugs(BugInfo bugInfo, out string message)
        {
            IdNamePair reviewStatus = bugInfo.CustomFields.Find(o => o.ID == "33");
            IdNamePair reviewContent = bugInfo.CustomFields.Find(o => o.ID == "32");
            IdNamePair reviewReviewer = bugInfo.CustomFields.Find(o => o.ID == "31");

            if (reviewStatus == null ||
                reviewContent == null ||
                reviewReviewer == null)
            {
                message = string.Format(Properties.Resources.ErrorDoNotSupportCodeReview, bugInfo.ID);
                return false;
            }

            message = string.Empty;
            return true;
        }

        public static bool IsCodeReviewBug(BugInfo bugInfo, string uid, out string message)
        {
            message = string.Empty;
            IdNamePair reviewStatus = bugInfo.CustomFields.Find(o => o.ID == "33");
            if (reviewStatus.Value != AppEnum.GetDescription(typeof(AppEnum.BugCodeReviewStatus), AppEnum.BugCodeReviewStatus.Reviewing))
            {
                message = string.Format(Properties.Resources.ErrorBugReviewing, bugInfo.ID);
                return false;
            }

            message = string.Format(Properties.Resources.ErrorBugReviewing, bugInfo.ID);
            return true;
        }

        public static List<ChangelistInfo> LoadCodeReviewChangelist(SearchBugs searchBugs)
        {
            List<ChangelistInfo> changelists = new List<ChangelistInfo>();
            string message;

            foreach(BugInfo var in searchBugs.Bugs)
            {
                if (Utility.SupportCodeReviewBugs(var, out message) == false ||
                    Utility.IsCodeReviewBug(var, var.AssignedTo.ID, out message) == false)
                {
                    continue;
                }

                IdNamePair reviewStatus = var.CustomFields.Find(o => o.ID == "33");
                IdNamePair reviewContent = var.CustomFields.Find(o => o.ID == "32");
                IdNamePair reviewReviewer = var.CustomFields.Find(o => o.ID == "31");

                if (reviewStatus.Value != AppEnum.GetDescription(typeof(AppEnum.BugCodeReviewStatus), AppEnum.BugCodeReviewStatus.Reviewing))
                {
                    continue;
                }

                ChangelistInfo changelist = new ChangelistInfo()
                {
                    Status = (int)AppEnum.ChangelistStatus.ReceiveReviewing,
                    Displayed = true
                };

                if (reviewReviewer.Value != Utility.Settings.MyInfo.ID)
                {
                    changelist.ErrorInfo = new ChanglistErrorInfo(AppEnum.ChangelistErrorCode.CodeReview, Properties.Resources.ErrorNotAssignToMeForReview);
                }

                try
                {
                    Utility.ParseReviewContent(reviewContent.Value, var, changelist, false);
                }
                catch (InvalidDataException ex)
                {
                    changelist.ErrorInfo = new ChanglistErrorInfo(AppEnum.ChangelistErrorCode.CodeReview, ex.Message);
                }

                ChangelistInfo existedChangelist = changelists.Find(o => o.StoredPath == changelist.StoredPath);
                if (existedChangelist == null)
                {
                    changelists.Add(changelist);
                }
                else
                {
                    changelist = existedChangelist;
                }

                changelist.BugInfos.Add(var);
            }

            return changelists;
        }

        public static bool IsBugBinding(BugInfo bugInfo, out string message)
        {
            message = string.Empty;
            if (Utility.IsOpenBugStatus(bugInfo, out message) == false)
            {
                message = string.Format(Properties.Resources.ErrorAssignedBug, bugInfo.ID);
                return true;
            }

            IdNamePair reviewStatus = bugInfo.CustomFields.Find(o => o.ID == "33");
            IdNamePair reviewContent = bugInfo.CustomFields.Find(o => o.ID == "32");

            int actionStatus;
            if (int.TryParse(bugInfo.ActionStatus, out actionStatus) == false)
            {
                return false;
            }

            if (string.IsNullOrEmpty(bugInfo.ActionStatus) == true ||
                actionStatus == (int)AppEnum.ChangelistStatus.CommitedButWaiting)
            {
                return false;
            }

            message = string.Format(Properties.Resources.ErrorAssignedBug, bugInfo.ID);
            return true;
        }

        private static void ParseReviewContent(string content, BugInfo bugInfo, ChangelistInfo changelist, bool local = true)
        {
            if (string.IsNullOrEmpty(content) == true)
            {
                return;
            }

            Regex regex = new Regex(@"\[([^\]]*)\]");
            MatchCollection match = regex.Matches(content);

            try
            {
                if (bugInfo != null)
                {
                    bugInfo.ActionStatus = match[4].Groups[1].Value;
                }

                if (changelist != null)
                {
                    if (local == false)
                    {
                        changelist.Name = match[1].Groups[1].Value;
                        changelist.WorkingCopyPath = match[2].Groups[1].Value;
                        changelist.Comment = match[5].Groups[1].Value;
                        changelist.StoredPath = match[3].Groups[1].Value;
                    }
                    else
                    {
                        changelist.FeedbackComment = match[5].Groups[1].Value;
                    }

                    changelist.SendedBy = Utility.Settings.Redmine.Members.Find(o => o.ID == match[0].Groups[1].Value);

                    if (changelist.SendedBy == null)
                    {
                        string name = Utility.GetMidString(content, "] ", @"\");
                        changelist.SendedBy = new MemberInfo()
                        {
                            ID = match[0].Groups[1].Value,
                            Name = name
                        };
                    }
                }
            }
            catch (Exception)
            {
                throw new InvalidDataException(string.Format(Properties.Resources.ErrorParseCodeReviewContent, Environment.NewLine, content));
            }
        }

        public static bool IsSameProject(string firstUrl, string secondUrl)
        {
            int index = firstUrl.LastIndexOf("?");

            if (index == -1)
            {
                if (firstUrl.Trim('/') == secondUrl.Trim('/'))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            firstUrl = firstUrl.Substring(0, index);

            index = secondUrl.LastIndexOf("?");

            if (index == -1)
            {
                if (firstUrl.Trim('/') == secondUrl.Trim('/'))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            secondUrl = firstUrl.Substring(0, index);

            return firstUrl.Trim('/') == secondUrl.Trim('/');
        }

        /*
        private static BugInfo ParseBugsPageXml(string html)
        {
            BugInfo bugInfo = new BugInfo();

            int index = 0;
            string innerText;

            try
            {
                #region BugID
                // Parse bug id.
                innerText = Utility.GetMidString(html, "<title>", "</title>", ref index);

                if (index == -1)
                {
                    return null;
                }

                string id = Utility.GetMidString(innerText, "#", ":");

                if (string.IsNullOrEmpty(id) == true)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.ID = id;
                #endregion

                #region ProjectID
                // Parse OpenBy.
                string projectUrl = Utility.GetMidString(html, "<option selected=\"selected\" value=\"", ">", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.ProjectID = Settings.Redmine.Projects.Where(o => Utility.IsSameProject(o.Url, projectUrl) == true).FirstOrDefault().ID;
                #endregion

                #region BugTitle
                // Parse bug title.
                innerText = Utility.GetMidString(html, "<div class=\"subject\">", "</div>", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }

                string title = Utility.GetMidString(innerText, "<h3>", "</h3>");
                if (string.IsNullOrEmpty(id) == true)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.Title = title;
                #endregion

                #region OpenBy
                // Parse OpenBy.
                string openBy = Utility.GetMidString(html, "/users/", "\"", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.OpenedBy = openBy;
                #endregion

                #region Status
                // Parse Status.
                string status = Utility.GetMidString(html, "<td class=\"status\">", "</td>", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.Status = (int)AppEnum.GetBugStatusByDescription(status);
                #endregion

                #region AssignTo
                // Parse AssignTo.
                string assignTo = Utility.GetMidString(html, "<td class=\"assigned-to\"><a href=\"/users/", "\">", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }
                bugInfo.AssignedTo = assignTo;
                #endregion

                #region BugDescription
                // Parse bug title.
                innerText = Utility.GetInnerText(html, "textarea", "issue_description", ref index);
                if (index == -1)
                {
                    // Get a error bug info page.
                    return null;
                }

                bugInfo.Description = innerText;
                #endregion
            }
            catch (ArgumentOutOfRangeException)
            {
                return bugInfo;
            }

            return bugInfo;
        }


        public static BugInfo GetBugInfo(string bugID)
        {
            return Utility.ParseBugsPageXml(Utility.GetBugPageHtml(bugID));
        }
         * */

        public static BugInfo GetBugInfo(string bugID)
        {
            string xml = System.Text.RegularExpressions.Regex.Replace(Utility.GetBugPageHtml(bugID), "^[^<]", "");

            BugInfo bugInfo = CommonManage.Utility.SerializedXml2Object<BugInfo>(Utility.GetBugPageHtml(bugID));

            if (bugInfo == null)
            {
                return null;
            }

            IdNamePair reviewContent = bugInfo.CustomFields.Find(o => o.ID == "32");

            Utility.ParseReviewContent(reviewContent.Value, bugInfo, null);

            return bugInfo;
        }

        #endregion

        #region CMD Execute


        public static void ExecuteCmd(string exec, string param)
        {
            CommonManage.Utility.ExecuteCmd(exec, param);
        }

        public static string ExecuteCmd(string cmd)
        {
            return CommonManage.Utility.ExecuteCmd(cmd);
        }

        public static string GetWorkingCopyUrl(string path)
        {
            SvnInfoEventArgs svnInfo;
            if (Utility.SvnClient.GetInfo(path, out svnInfo) == false)
            {
                throw new InvalidDataException(Properties.Resources.ErrorWorkingCopyExists);
            }

            return svnInfo.Uri.AbsolutePath;
        }

        public static void UpdateChangelistBugs(ChangelistInfo changelistInfo, SearchBugs searchBugs)
        {
            if (changelistInfo.Status == (int)AppEnum.ChangelistStatus.Disabled ||
                changelistInfo.Displayed == false)
            {
                return;
            }

            changelistInfo.ClearError();

            BugInfo[] bugs = new BugInfo[changelistInfo.BugInfos.Count];
            changelistInfo.BugInfos.CopyTo(bugs);

            foreach (BugInfo bug in bugs)
            {
                BugInfo update = searchBugs.Bugs.Find(o => o.ID == bug.ID);

                if (update == null &&
                    changelistInfo.Status == (int)AppEnum.ChangelistStatus.SendReviewing)
                {
                    update = Utility.GetBugInfo(bug.ID.ToString());
                }

                if (update == null)
                {
                    changelistInfo.ErrorInfo = new ChanglistErrorInfo(
                        AppEnum.ChangelistErrorCode.BugAssigned,
                        string.Format(Properties.Resources.ErrorGetBugsInfo, bug.ID));
                    update = bug;
                }

                changelistInfo.BugInfos.Remove(bug);
                changelistInfo.BugInfos.Add(update);

                IdNamePair reviewStatus = update.CustomFields.Find(o => o.ID == "33");
                IdNamePair reviewContent = update.CustomFields.Find(o => o.ID == "32");
                Utility.ParseReviewContent(reviewContent.Value, update, changelistInfo);

                if (changelistInfo.Status == (int)AppEnum.ChangelistStatus.SendReviewing)
                {
                    if (reviewStatus.Value == AppEnum.GetDescription(typeof(AppEnum.BugCodeReviewStatus), AppEnum.BugCodeReviewStatus.Reviewing))
                    {
                        continue;
                    }

                    if (reviewStatus.Value == AppEnum.GetDescription(typeof(AppEnum.BugCodeReviewStatus), AppEnum.BugCodeReviewStatus.None))
                    {
                        changelistInfo.ErrorInfo = new ChanglistErrorInfo(
                            AppEnum.ChangelistErrorCode.CodeReview,
                            string.Format(Properties.Resources.ErrorCodeReviewStatus, reviewStatus.Value));
                        return;
                    }

                    if (update.AssignedTo.ID != Utility.Settings.MyInfo.ID)
                    {
                        changelistInfo.ErrorInfo = new ChanglistErrorInfo(
                            AppEnum.ChangelistErrorCode.CodeReview,
                            string.Format(Properties.Resources.ErrorNotYourBugs, update.ID, update.AssignedTo.Name));
                        return;
                    }

                    if (reviewStatus.Value == AppEnum.GetDescription(typeof(AppEnum.BugCodeReviewStatus), AppEnum.BugCodeReviewStatus.Approved))
                    {
                        if (string.IsNullOrEmpty(changelistInfo.FeedbackComment) == true)
                        {
                            changelistInfo.Status = (int)AppEnum.ChangelistStatus.Approved;
                        }
                        else
                        {
                            changelistInfo.Status = (int)AppEnum.ChangelistStatus.ApprovedWithComment;
                        }
                    }
                    else
                    {
                        changelistInfo.Status = (int)AppEnum.ChangelistStatus.Rejected;
                    }
                }
            }
        }

        public static void UpdateAllLocalWorkingCopyBugs(WorkingCopyInfo workingCopyInfo, SearchBugs searchBugs)
        {
            foreach (ChangelistInfo var in workingCopyInfo.ChangelistInfos)
            {
                Utility.UpdateChangelistBugs(var, searchBugs);
            }
        }

        public static void GetAllChangeLists(WorkingCopyInfo workingCopyInfo)
        {
            if (workingCopyInfo == null)
            {
                return;
            }

            Collection<SvnListChangeListEventArgs> eventArgs = new Collection<SvnListChangeListEventArgs>();

            Utility.SvnClient.GetChangeList(workingCopyInfo.Path, out eventArgs);

            string[] changelists = eventArgs.Where(o => string.IsNullOrEmpty(o.ChangeList) == false).Select(o => o.ChangeList).Distinct().ToArray();

            foreach (string var in changelists)
            {
                ChangelistInfo info = workingCopyInfo.ChangelistInfos.Find(o => o.Name == var && o.WorkingCopyPath == workingCopyInfo.Path);

                if (info == null)
                {
                    info = new ChangelistInfo()
                    {
                        Name = var,
                        WorkingCopyPath = workingCopyInfo.Path,
                        Status = (int)AppEnum.ChangelistStatus.Local,
                        Displayed = true
                    };

                    workingCopyInfo.ChangelistInfos.Add(info);
                }
                else
                {
                    info.ClearError();
                }
            }

            foreach (ChangelistInfo var in workingCopyInfo.ChangelistInfos)
            {
                if (changelists.Contains(var.Name) == false)
                {
                    if (var.Status == (int)AppEnum.ChangelistStatus.SendReviewing)
                    {
                        var.ErrorInfo = new ChanglistErrorInfo(AppEnum.ChangelistErrorCode.CodeReview, Properties.Resources.ErrorChangelistDisappearWhenCodeReview);
                    }
                    else
                    {
                        var.Status = (int)AppEnum.ChangelistStatus.Disabled;
                    }
                }
                else
                {
                    if (var.WorkingCopyPath != workingCopyInfo.Path)
                    {
                        var.Status = (int)AppEnum.ChangelistStatus.Disabled;
                    }
                }
            }

            /*
            string cmdResult = Utility.ExecuteCmd(string.Format(AppConst.ChangeListCmd, workingCopyInfo.Path));
            workingCopyInfo.ChangelistInfos.ForEach(o => { o.Enabled = false; });
            int index = 0;
            string name = string.Empty;
            while (true)
            {
                name = Utility.GetMidString(cmdResult, AppConst.KeywordChangeListNameStart, AppConst.KeywordChangeListNameEnd, ref index);

                if (index == -1)
                {
                    break;
                }

                ChangelistInfo info = workingCopyInfo.ChangelistInfos.Find(o => o.Name == name && o.WorkingCopyPath == workingCopyInfo.Path);

                if (info == null)
                {
                    info = new ChangelistInfo()
                    {
                        Name = name,
                        WorkingCopyPath = workingCopyInfo.Path,
                        Enabled = true,
                        Displayed = true
                    };

                    workingCopyInfo.ChangelistInfos.Add(info);
                }
                else
                {
                    info.Enabled = true;
                }
            }
             * */
        }

        public static List<SvnStatusEventArgs> GetUnControledFiles(string workingCopyPath)
        {
            if (string.IsNullOrEmpty(workingCopyPath) == true)
            {
                return null;
            }

            SvnStatusArgs statusArgs = new SvnStatusArgs()
            {
                Depth = SvnDepth.Infinity,
                RetrieveRemoteStatus = true
            };

            Collection<SvnStatusEventArgs> eventArgs = new Collection<SvnStatusEventArgs>();
            Utility.SvnClient.GetStatus(workingCopyPath, out eventArgs);

            List<SvnStatusEventArgs> filesSvnStatus = eventArgs.Where(o => o.NodeKind != SvnNodeKind.Directory && string.IsNullOrEmpty(o.ChangeList) == true && o.FileLength >= 0).ToList();

            return filesSvnStatus;
        }

        public static List<SvnStatusEventArgs> GetChangeListCommitFiles(ChangelistInfo changelistInfo, out List<SvnStatusEventArgs> directoriesSvnStatus)
        {
            SvnStatusArgs statusArgs = new SvnStatusArgs()
            {
                Depth = SvnDepth.Infinity,
                RetrieveRemoteStatus = true
            };

            Collection<SvnStatusEventArgs> eventArgs = new Collection<SvnStatusEventArgs>();
            Utility.SvnClient.GetStatus(changelistInfo.WorkingCopyPath, out eventArgs);

            List<SvnStatusEventArgs> allDirectoriesSvnStatus = eventArgs.Where(o => o.NodeKind == SvnNodeKind.Directory).ToList();
            directoriesSvnStatus = new List<SvnStatusEventArgs>();

            List<SvnStatusEventArgs> filesSvnStatus = eventArgs.Where(o => o.NodeKind == SvnNodeKind.File && changelistInfo.Name == o.ChangeList).ToList();

            foreach (SvnStatusEventArgs var in allDirectoriesSvnStatus)
            {
                if (filesSvnStatus.Any(o => o.Path.Contains(var.Path + Path.DirectorySeparatorChar) == true) == true)
                {
                    if (var.LocalNodeStatus == SvnStatus.Added ||
                        var.LocalNodeStatus == SvnStatus.Deleted ||
                        var.LocalNodeStatus == SvnStatus.NotVersioned ||
                        var.LocalNodeStatus == SvnStatus.Missing)
                    {
                        directoriesSvnStatus.Add(var);
                    }
                }
            }

            if (directoriesSvnStatus.Count > 1)
            {
                directoriesSvnStatus = directoriesSvnStatus.OrderBy(o => o.FullPath.Length).ToList();
            }

            return filesSvnStatus;
        }

        public static string TraceException(Exception ex)
        {
            StringBuilder message = new StringBuilder();
            message.AppendLine(ex.Message);

            int num1 = 0;
            while ((ex.InnerException != null) && (num1 < 0x19))
            {
                ex = ex.InnerException;
                message.AppendLine(ex.Message);
            }

            return message.ToString();
        }

        public static CommonBasic.DSFileDiffInfo GetChangeListDiffInfo(ChangelistInfo changelistInfo, bool isLocal = true)
        {
            CommonBasic.DSFileDiffInfo diffInfo = new CommonBasic.DSFileDiffInfo()
            {
                LocalDiff = isLocal,
                WorkingCopyPath = changelistInfo.WorkingCopyPath,
                DiffInfos = new List<CommonBasic.DSFileDiffUnitInfo>()
            };

            SvnStatusArgs statusArgs = new SvnStatusArgs()
            {
                Depth = SvnDepth.Infinity,
                RetrieveRemoteStatus = true
            };

            statusArgs.ChangeLists.Add(changelistInfo.Name);
            Collection<SvnStatusEventArgs> eventArgs = new Collection<SvnStatusEventArgs>();
            Utility.SvnClient.GetStatus(changelistInfo.WorkingCopyPath, statusArgs, out eventArgs);

            foreach (SvnStatusEventArgs var in eventArgs)
            {
                if (var.ChangeList != changelistInfo.Name)
                {
                    continue;
                }

                if (var.LocalNodeStatus == SvnStatus.Missing ||
                    var.LocalNodeStatus == SvnStatus.Normal ||
                    var.LocalNodeStatus == SvnStatus.NotVersioned)
                {
                    continue;
                }

                CommonBasic.DSFileDiffUnitInfo diffUnitInfo = new CommonBasic.DSFileDiffUnitInfo();
                diffUnitInfo.Status = (int)var.LocalNodeStatus;
                diffUnitInfo.WorkingCopyPath = changelistInfo.WorkingCopyPath;
                diffUnitInfo.DisplayPath = var.FullPath;

                string leftPath = string.Empty;
                string rightPath = string.Empty;
                long revision = var.LastChangeRevision;

                if (isLocal == false)
                {
                    if (var.LocalNodeStatus != SvnStatus.Added)
                    {
                        if (var.LocalNodeStatus == SvnStatus.Deleted &&
                            var.LastChangeRevision < 0)
                        {
                            SvnInfoEventArgs infoArgs;
                            Utility.SvnClient.GetInfo(var.FullPath, out infoArgs);
                            revision = infoArgs.LastChangeRevision;
                        }

                        string tempFileFullName = CommonManage.Utility.GetStudioTempFullFileName(var.FullPath.Replace(changelistInfo.WorkingCopyPath, ""), revision);

                        SvnWriteArgs wa = new SvnWriteArgs();
                        wa.Revision = revision;
                        wa.AddExpectedError(SvnErrorCode.SVN_ERR_CLIENT_UNRELATED_RESOURCES);

                        bool unrelated = false;
                        using (Stream s = File.Create(tempFileFullName))
                        {
                            if (!Utility.SvnClient.Write(var.FullPath, s, wa))
                            {
                                if (wa.LastException.SvnErrorCode == SvnErrorCode.SVN_ERR_CLIENT_UNRELATED_RESOURCES)
                                    unrelated = true;
                            }
                        }

                        if (unrelated == true)
                        {
                            throw new SvnClientUnrelatedResourcesException(Properties.Resources.ErorrRetrieveWorkBaseFile);
                        }

                        leftPath = tempFileFullName;
                    }

                    //Right
                    if (var.LocalNodeStatus != SvnStatus.Deleted)
                    {
                        rightPath = var.FullPath;
                    }
                }
                else
                {
                    // 对于local diff, 只用识别右边就好。由Tortoise svn来获取左边对比文件信息
                    rightPath = var.FullPath;
                }

                diffUnitInfo.Left = new CommonBasic.DSFileInfo()
                {
                    Path = leftPath,
                    Key = leftPath,
                    Revision = revision
                };

                diffUnitInfo.Right = new CommonBasic.DSFileInfo()
                {
                    Path = rightPath,
                    Key = rightPath,
                    Revision = CommonBasic.AppConst.LocalRevision
                };

                diffInfo.DiffInfos.Add(diffUnitInfo);
            }

            return diffInfo;
        }

        public static bool PackageDiffInfo(ChangelistInfo changelistInfo, string fileFullName, bool isLocal = true)
        {
            if (string.IsNullOrEmpty(fileFullName) == true)
            {
                return false;
            }

            if (Path.GetExtension(fileFullName) != CommonBasic.AppConst.DiffToolFileSuffix)
            {
                return false;
            }

            if (Directory.Exists(Path.GetDirectoryName(fileFullName)) == false)
            {
                return false;
            }

            CommonBasic.DSFileDiffInfo diffInfo = Utility.GetChangeListDiffInfo(changelistInfo, isLocal);
            diffInfo.Pack(fileFullName);

            return true;
        }

        #endregion


        public static bool IsNetworkPath(string path)
        {
            string origPath = path;
            path = path.Trim();

            Regex regexNet = new Regex(RegExLibrary.GetRegularExpression(RegExExpression.NetworkPath));

            if (regexNet.IsMatch(path) == false)
            {
                return false;
            }
            else if (path.Equals("\\\\") == true ||
                path.Equals("\\") == true ||
                (path.Split('\\').Length == 3 ||
                (path.Split('\\').Length == 4 &&
                String.IsNullOrEmpty(path.Split('\\')[3]) == true)))
            {
                return false;
            }
            else if (path.Substring(2).IndexOf("\\\\", StringComparison.OrdinalIgnoreCase) != -1)
            {
                return false;
            }
            else if (path != origPath)
            {
                return false;
            }

            return true;
        }
    }
}
